#include <stdio.h>
#include "main.h"

/* Used by some code below as an example datatype. */
struct record
{
    const char *precision;
    double lat;
    double lon;
    const char *address;
    const char *city;
    const char *state;
    const char *zip;
    const char *country;
};
/* Create a bunch of objects as demonstration. */
static int print_preallocated(cJSON *root)
{
    /* declarations */
    char *out = NULL;
    char *buf = NULL;
    char *buf_fail = NULL;
    size_t len = 0;
    size_t len_fail = 0;

    /* formatted print */
    out = cJSON_Print(root);

    /* create buffer to succeed */
    /* the extra 5 bytes are because of inaccuracies when reserving memory */
    len = strlen(out) + 5;
    buf = (char*)malloc(len);
    if (buf == NULL)
    {
        elog_raw("Failed to allocate memory.\n");
        exit(1);
    }

    /* create buffer to fail */
    len_fail = strlen(out);
    buf_fail = (char*)malloc(len_fail);
    if (buf_fail == NULL)
    {
        elog_raw("Failed to allocate memory.\n");
        exit(1);
    }

    /* Print to buffer */
    if (!cJSON_PrintPreallocated(root, buf, (int)len, 1)) {
        elog_raw("cJSON_PrintPreallocated failed!\n");
        if (strcmp(out, buf) != 0) {
            elog_raw("cJSON_PrintPreallocated not the same as cJSON_Print!\n");
            elog_raw("cJSON_Print result:\n%s\n", out);
            elog_raw("cJSON_PrintPreallocated result:\n%s\n", buf);
        }
        free(out);
        free(buf_fail);
        free(buf);
        return -1;
    }

    /* success */
    elog_raw("%s\n", buf);

    /* force it to fail */
    if (cJSON_PrintPreallocated(root, buf_fail, (int)len_fail, 1)) {
        elog_raw("cJSON_PrintPreallocated failed to show error with insufficient memory!\n");
        elog_raw("cJSON_Print result:\n%s\n", out);
        elog_raw("cJSON_PrintPreallocated result:\n%s\n", buf_fail);
        free(out);
        free(buf_fail);
        free(buf);
        return -1;
    }

    free(out);
    free(buf_fail);
    free(buf);
    return 0;
}

/* Create a bunch of objects as demonstration. */
static void create_objects(void)
{
    /* declare a few. */
    cJSON *root = NULL;
    cJSON *fmt = NULL;
    cJSON *img = NULL;
    cJSON *thm = NULL;
    cJSON *fld = NULL;
    int i = 0;

    /* Our "days of the week" array: */
    const char *strings[7] =
    {
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday"
    };
    /* Our matrix: */
    int numbers[3][3] =
    {
        {0, -1, 0},
        {1, 0, 0},
        {0 ,0, 1}
    };
    /* Our "gallery" item: */
    int ids[4] = { 116, 943, 234, 38793 };
    /* Our array of "records": */
    struct record fields[2] =
    {
        {
            "zip",
            37.7668,
            -1.223959e+2,
            "",
            "SAN FRANCISCO",
            "CA",
            "94107",
            "US"
        },
        {
            "zip",
            37.371991,
            -1.22026e+2,
            "",
            "SUNNYVALE",
            "CA",
            "94085",
            "US"
        }
    };
    volatile double zero = 0.0;

    /* Here we construct some JSON standards, from the JSON site. */

    /* Our "Video" datatype: */
    root = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
    cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject());
    cJSON_AddStringToObject(fmt, "type", "rect");
    cJSON_AddNumberToObject(fmt, "width", 1920);
    cJSON_AddNumberToObject(fmt, "height", 1080);
    cJSON_AddFalseToObject (fmt, "interlace");
    cJSON_AddNumberToObject(fmt, "frame rate", 24);

    /* Print to text */
    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our "days of the week" array: */
    root = cJSON_CreateStringArray(strings, 7);

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our matrix: */
    root = cJSON_CreateArray();
    for (i = 0; i < 3; i++)
    {
        cJSON_AddItemToArray(root, cJSON_CreateIntArray(numbers[i], 3));
    }

    /* cJSON_ReplaceItemInArray(root, 1, cJSON_CreateString("Replacement")); */

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our "gallery" item: */
    root = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "Image", img = cJSON_CreateObject());
    cJSON_AddNumberToObject(img, "Width", 800);
    cJSON_AddNumberToObject(img, "Height", 600);
    cJSON_AddStringToObject(img, "Title", "View from 15th Floor");
    cJSON_AddItemToObject(img, "Thumbnail", thm = cJSON_CreateObject());
    cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943");
    cJSON_AddNumberToObject(thm, "Height", 125);
    cJSON_AddStringToObject(thm, "Width", "100");
    cJSON_AddItemToObject(img, "IDs", cJSON_CreateIntArray(ids, 4));

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    /* Our array of "records": */
    root = cJSON_CreateArray();
    for (i = 0; i < 2; i++)
    {
        cJSON_AddItemToArray(root, fld = cJSON_CreateObject());
        cJSON_AddStringToObject(fld, "precision", fields[i].precision);
        cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat);
        cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon);
        cJSON_AddStringToObject(fld, "Address", fields[i].address);
        cJSON_AddStringToObject(fld, "City", fields[i].city);
        cJSON_AddStringToObject(fld, "State", fields[i].state);
        cJSON_AddStringToObject(fld, "Zip", fields[i].zip);
        cJSON_AddStringToObject(fld, "Country", fields[i].country);
    }

    /* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root, 1), "City", cJSON_CreateIntArray(ids, 4)); */

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);

    root = cJSON_CreateObject();
    cJSON_AddNumberToObject(root, "number", 1.0 / zero);

    if (print_preallocated(root) != 0) {
        cJSON_Delete(root);
        exit(EXIT_FAILURE);
    }
    cJSON_Delete(root);
}

/**
 * EasyLogger demo
 */
void test_elog(void) {

    while(true) {
        /* test log output for all level */
        log_a("Hello EasyLogger!");
        log_e("Hello EasyLogger!");
        log_w("Hello EasyLogger!");
        log_i("Hello EasyLogger!");
        log_d("Hello EasyLogger!");
        log_v("Hello EasyLogger!");
        elog_raw("Hello EasyLogger!");
        // elog_hexdump("test", 16, buf, sizeof(buf));
        usleep(500000);
    }
    
}

int main(int argc, char* argv[])
{
    /* initialize EasyLogger */
    elog_init();
    /* set EasyLogger log format */
    elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL);
    elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
    elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
    elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
    elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~ELOG_FMT_FUNC);
    elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL & ~ELOG_FMT_FUNC);
#ifdef ELOG_COLOR_ENABLE
    elog_set_text_color_enabled(true);
#endif
    /* start EasyLogger */
    elog_start();
    /* dynamic set enable or disable for output logs (true or false) */
   elog_set_output_enabled(true);
    /* dynamic set output logs's level (from ELOG_LVL_ASSERT to ELOG_LVL_VERBOSE) */
   elog_set_filter_lvl(ELOG_LVL_DEBUG); // 限制输出等级最低debug
    /* dynamic set output logs's filter for tag */
//    elog_set_filter_tag("main"); // 设置main的log不输出
    /* dynamic set output logs's filter for keyword */
//    elog_set_filter_kw("Hello"); //输出带有key的log 过滤掉不带key的log
    /* dynamic set output logs's tag filter */
//    elog_set_filter_tag_lvl("main", ELOG_LVL_DEBUG); // 限制main标签的输出log等级
    // elog_buf_enabled(true); // 缓冲buff 缓冲到指定数量开始输出
    /* print the version */
    log_d("cJson Version: %s\n", cJSON_Version());
    // test_elog();

    device_print(); // 打印设备架构

    create_objects(); // cjson测试

#if UUID // uuid测试
    uuid_t		buf, tst;
	char		str[100];
	struct timeval	tv;
	time_t		time_reg;
	unsigned char	*cp;
	int i;
	int failed = 0;
	int type, variant;

	uuid_generate(buf);
	uuid_unparse(buf, str);
	printf("UUID generate = %s\n", str);
	printf("UUID: ");
	for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
		printf("%02x", *cp++);
	}
	printf("\n");
	type = uuid_type(buf); 	variant = uuid_variant(buf);
	printf("UUID type = %d, UUID variant = %d\n", type, variant);
	if (variant != UUID_VARIANT_DCE) {
		printf("Incorrect UUID Variant; was expecting DCE!\n");
		failed++;
	}
	printf("\n");

	uuid_generate_random(buf);
	uuid_unparse(buf, str);
	printf("UUID random string = %s\n", str);
	printf("UUID: ");
	for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
		printf("%02x", *cp++);
	}
	printf("\n");
	type = uuid_type(buf); 	variant = uuid_variant(buf);
	printf("UUID type = %d, UUID variant = %d\n", type, variant);
	if (variant != UUID_VARIANT_DCE) {
		printf("Incorrect UUID Variant; was expecting DCE!\n");
		failed++;
	}
	if (type != 4) {
		printf("Incorrect UUID type; was expecting "
		       "4 (random type)!\n");
		failed++;
	}
	printf("\n");

	uuid_generate_time(buf);
	uuid_unparse(buf, str);
	printf("UUID string = %s\n", str);
	printf("UUID time: ");
	for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
		printf("%02x", *cp++);
	}
	printf("\n");
	type = uuid_type(buf); 	variant = uuid_variant(buf);
	printf("UUID type = %d, UUID variant = %d\n", type, variant);
	if (variant != UUID_VARIANT_DCE) {
		printf("Incorrect UUID Variant; was expecting DCE!\n");
		failed++;
	}
	if (type != 1) {
		printf("Incorrect UUID type; was expecting "
		       "1 (time-based type)!\\n");
		failed++;
	}
	tv.tv_sec = 0;
	tv.tv_usec = 0;
	time_reg = uuid_time(buf, &tv);
	printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec,
	       ctime(&time_reg));
	uuid_parse(str, tst);
	if (!uuid_compare(buf, tst))
		printf("UUID parse and compare succeeded.\n");
	else {
		printf("UUID parse and compare failed!\n");
		failed++;
	}
	uuid_clear(tst);
	if (uuid_is_null(tst))
		printf("UUID clear and is null succeeded.\n");
	else {
		printf("UUID clear and is null failed!\n");
		failed++;
	}
	uuid_copy(buf, tst);
	if (!uuid_compare(buf, tst))
		printf("UUID copy and compare succeeded.\n");
	else {
		printf("UUID copy and compare failed!\n");
		failed++;
	}
    #endif

 #if SQLITE
    sqlite3 *db;    

    const char *sql = 0;
    char *zErrMsg = 0;          //错误信息储存
    int nrow = 0, ncolumn = 0;  //行和列
    char **azResult; 	        //二维数组存放结果
	
    int rc = 0;

    rc = sqlite3_open("test.db", &db);
    if( rc )
    {
	  fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
	  sqlite3_close(db);
	  return 1;
    }
    else
	  log_d("Open a sqlite3 database named rtu_data.db successfully!\n");
//创建一个表,如果该表存在，则不创建，并给出提示信息，存储在 zErrMsg 中
    sql = "create table data_table(rtuID, registerNum, data);";
    sqlite3_exec(db, sql, 0, 0, &zErrMsg);
 
    log_e("zErrMsg = %s \n", zErrMsg);

    //插入数据: 2种方法 
    sql = "insert into data_table(rtuID, registerNum, data) values(01, 0001, 1.0);";
    sqlite3_exec(db, sql, 0, 0, &zErrMsg);
 
    sql = "insert into \"data_table\" values(02, 0002, 2.0);";
    sqlite3_exec(db, sql, 0, 0, &zErrMsg);
 
    sql = "select * from data_table;";
    sqlite3_get_table(db, sql, &azResult, &nrow, &ncolumn, &zErrMsg);
 
    log_d("row=%d, column=%d\n", nrow, ncolumn);

    log_d("The result of querying is:");
    for(int i=0; i<(nrow + 1) * ncolumn; i++)
	  log_d("azResult[%d] = %s\n", i, azResult[i]);
 
    //释放掉 azResult 的内存空间
    sqlite3_free_table(azResult);
	
    log_e("zErrMsg = %s \n", zErrMsg);

     sqlite3_close(db); 
   #endif

   #if 1 // libmodbus
    log_d("Compiled with libmodbus version %s (%06X)\n",
           LIBMODBUS_VERSION_STRING,
           LIBMODBUS_VERSION_HEX);
    log_d("Linked with libmodbus version %d.%d.%d\n",
           libmodbus_version_major,
           libmodbus_version_minor,
           libmodbus_version_micro);

    if (LIBMODBUS_VERSION_CHECK(2, 1, 0)) {
        log_d("The functions to read/write float values are available (2.1.0).\n");
    }

    if (LIBMODBUS_VERSION_CHECK(2, 1, 1)) {
        log_d("Oh gosh, brand new API (2.1.1)!\n");
    }

    modbus_t* ctx; 
     int slaveAddr=1;
     //建立modbus buf
     uint8_t dest[1024]; //setup memory for data
     uint16_t * dest16 = (uint16_t *) dest;
     memset(dest, 0, 1024);
     //初始化Modbus实例
     ctx = modbus_new_rtu("/dev/ttyUSB0", 9600, 'N', 8, 1);// 使用9600-N-8，1个停止位
     if (modbus_connect(ctx) == -1) {
         log_d("Connexion failed: %s\n", modbus_strerror(errno));
         modbus_free(ctx);
         return -1;
     }
 
     modbus_set_slave(ctx, slaveAddr);//设置从站地址，当前为1
     modbus_set_response_timeout(ctx, 1, 0);//设置响应时间
     /*
      * modbus_read_bits  读线圈  fc=1
      * modbus_read_input_bits 读输入线圈 fc=2
      * modbus_read_registers 读取保持寄存器 fc=3
      * modbus_read_input_registers 读输入寄存器 fc=4
      * modbus_write_bit 写一位数据（强置单线圈） fc=5
      * modbus_write_register 写单寄存器（预置单寄存器） fc=6
      * modbus_write_bits 写多位数据（强置多线圈） fc=15
      * modbus_write_and_read_registers 读/写多个寄存器 fc=23
     */
     int regs = modbus_read_input_registers(ctx/*libmodbus实例*/, 
                                            0/*输入地址*/, 
                                            8/*读取输入寄存器个数*/, 
                                            dest16 /*获取从站值*/);
     modbus_close(ctx);
     modbus_free(ctx);//关闭 清理 modbus ctx
   #endif

        return 0;
}
